﻿using System;
using System.Collections;
using System.Collections.Generic;
using Nomo.CoreModule;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Nomo.UnityCoreModule.NodeBehaviours
{
    /// <summary>
    /// 界面。
    /// </summary>
    /// <remarks>
    /// 严禁对界面执行下列操作：
    /// 一、使用 <see cref="GameObject.SetActive"/> 改变界面所在的游戏物体的激活状态；
    /// 二、使用 <see cref="UnityEngine.Object.Destroy(UnityEngine.Object)"/> 或它的其它重载方法销毁界面所在的游戏物体；
    /// 三、使用 <see cref="UnityEngine.Object.Instantiate(UnityEngine.Object)"/> 或它的其它重载方法实例化界面的副本。
    /// </remarks>
    [DisallowMultipleComponent]
    [RequireComponent(typeof(RectTransform))]
    [RequireComponent(typeof(CanvasGroup))]
    public abstract class NodeBehaviour : UIBehaviour, ICanvasElement
    {
        #region static member

        /// <summary>
        /// 顺序。
        /// </summary>
        public enum Order
        {
            /// <summary>
            /// 前序。
            /// </summary>
            Pre,

            /// <summary>
            /// 层序。
            /// </summary>
            Level,

            /// <summary>
            /// 倒转的前序。
            /// </summary>
            ReversedPre
        }

        /// <summary>
        /// 表示界面的状态。界面的状态一定是定义的值，在获取界面时可以传入由多个定义的值复合后的结果。
        /// </summary>
        [Flags]
        public enum NodeState
        {
            /// <summary>
            /// 该界面是资源包内的预制体，不能直接作为界面使用。
            /// </summary>
            None = 0,

            /// <summary>
            /// 该界面所在的游戏物体未激活。
            /// </summary>
            Inactive = 1 << 0,

            /// <summary>
            /// 该界面所在的游戏物体已激活，并且处于入场过程中。
            /// </summary>
            Enter = 1 << 1,

            /// <summary>
            /// 该界面所在的游戏物体已激活，并且不处于入场或出场过程中。
            /// </summary>
            Active = 1 << 2,

            /// <summary>
            /// 该界面所在的游戏物体已激活，并且处于出场过程中。
            /// </summary>
            Exit = 1 << 3
        }

        /// <summary>
        /// 销毁类型。
        /// </summary>
        public enum DestroyType
        {
            /// <summary>
            /// 不销毁。
            /// </summary>
            None,

            /// <summary>
            /// 销毁（并不立即销毁，而是标记对象为等待销毁，在当前帧的末尾再执行）。
            /// </summary>
            Destroy,

            /// <summary>
            /// 立即销毁。
            /// </summary>
            DestroyImmediately,
        }

        /// <summary>
        /// 过渡参数。
        /// </summary>
        public class TransitionArgs
        {
            /// <summary>
            /// 立即地。
            /// </summary>
            public bool Immediately;
        }

        /// <summary>
        /// 打开参数。继承此类以便在打开界面时传入更多自定义数据。
        /// </summary>
        public class OpenArgs : TransitionArgs { }

        /// <summary>
        /// 关闭参数。继承此类以便在关闭界面时传入更多自定义数据。
        /// </summary>
        public class CloseArgs : TransitionArgs
        {
            /// <summary>
            /// 关闭完成后销毁所在的游戏物体。
            /// </summary>
            public DestroyType PostDestroyType;
        }

        private static NodeBehaviourContainer      _rootNodeBehaviourContainer;
        private static INodeBehaviourModelProvider _nodeBehaviourModelProviderForCreateNodeBehaviour;

        private static readonly Comparison<NodeBehaviour> LevelOrderComparison = LevelOrderComparisonMethod;

        private static int LevelOrderComparisonMethod(NodeBehaviour nodeBehaviour1, NodeBehaviour nodeBehaviour2)
        {
            return ParentCount(nodeBehaviour1.transform) - ParentCount(nodeBehaviour2.transform);
        }

        private static int ParentCount(Transform transform)
        {
            var count  = 0;
            var parent = transform.parent;
            while (parent != null)
            {
                ++count;
                parent = parent.parent;
            }
            return count;
        }

        public static void Init(NodeBehaviourContainer rootNodeBehaviourContainer, INodeBehaviourModelProvider nodeBehaviourModelProviderForCreateNodeBehaviour)
        {
            if (rootNodeBehaviourContainer == null)
            {
                throw new ArgumentNullException(nameof(rootNodeBehaviourContainer));
            }
            if (nodeBehaviourModelProviderForCreateNodeBehaviour == null)
            {
                throw new ArgumentNullException(nameof(nodeBehaviourModelProviderForCreateNodeBehaviour));
            }

            _rootNodeBehaviourContainer                       = rootNodeBehaviourContainer;
            _nodeBehaviourModelProviderForCreateNodeBehaviour = nodeBehaviourModelProviderForCreateNodeBehaviour;
        }

        /// <summary>
        /// 按照指定的顺序获取处于指定状态的指定类型的所有界面。
        /// </summary>
        /// <param name="order">顺序。</param>
        /// <param name="state">状态。它可以是多个状态进行按位与运算后的结果。</param>
        /// <param name="result">存放结果的列表。</param>
        /// <typeparam name="TNodeBehaviour">界面的类型。</typeparam>
        /// <exception cref="ArgumentNullException"><paramref name="result"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="state"/> 为 <see cref="NodeState.None"/> 或 <paramref name="order"/> 为未定义的值。</exception>
        public static void Get<TNodeBehaviour>(Order order, NodeState state, List<TNodeBehaviour> result) where TNodeBehaviour : NodeBehaviour
        {
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }
            if (state == NodeState.None)
            {
                throw new ArgumentOutOfRangeException(nameof(state), state, null);
            }
            result.Clear();
            if (_rootNodeBehaviourContainer != null)
            {
                var list = ListPool<NodeBehaviour>.Get();
                _rootNodeBehaviourContainer.GetComponentsInChildren(true, list);
                switch (order)
                {
                    case Order.Pre:
                        break;
                    case Order.Level:
                        list.Sort(LevelOrderComparison);
                        break;
                    case Order.ReversedPre:
                        list.Reverse();
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(order), order, null);
                }
                foreach (var element in list)
                {
                    var tNodeBehaviour      = (TNodeBehaviour) element;
                    var tNodeBehaviourState = tNodeBehaviour.State;
                    if ((state & tNodeBehaviourState) == tNodeBehaviourState)
                    {
                        result.Add(tNodeBehaviour);
                    }
                }
                ListPool<NodeBehaviour>.Release(list);
            }
        }

        /// <summary>
        /// 按照指定的顺序获取指定界面上和指定界面的子界面中处于指定状态的指定类型的所有界面。
        /// </summary>
        /// <param name="nodeBehaviour">界面。</param>
        /// <param name="order">顺序。</param>
        /// <param name="state">状态。它可以是多个状态进行按位与运算后的结果。</param>
        /// <param name="result">存放结果的列表。</param>
        /// <typeparam name="TNodeBehaviour">界面的类型。</typeparam>
        /// <exception cref="ArgumentNullException"><paramref name="nodeBehaviour"/> 或 <paramref name="result"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="state"/> 为 <see cref="NodeState.None"/> 或 <paramref name="order"/> 为未定义的值。</exception>
        public static void Get<TNodeBehaviour>(NodeBehaviour nodeBehaviour, Order order, NodeState state, List<TNodeBehaviour> result) where TNodeBehaviour : NodeBehaviour
        {
            if (nodeBehaviour == null)
            {
                throw new ArgumentNullException(nameof(nodeBehaviour));
            }
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }
            if (state == NodeState.None)
            {
                throw new ArgumentOutOfRangeException(nameof(state), state, null);
            }
            result.Clear();
            var list = ListPool<NodeBehaviour>.Get();
            nodeBehaviour.GetComponentsInChildren(true, list);
            switch (order)
            {
                case Order.Pre:
                    break;
                case Order.Level:
                    list.Sort(LevelOrderComparison);
                    break;
                case Order.ReversedPre:
                    list.Reverse();
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(order), order, null);
            }
            foreach (var element in list)
            {
                var tNodeBehaviour      = (TNodeBehaviour) element;
                var tNodeBehaviourState = tNodeBehaviour.State;
                if ((state & tNodeBehaviourState) == tNodeBehaviourState)
                {
                    result.Add(tNodeBehaviour);
                }
            }
            ListPool<NodeBehaviour>.Release(list);
        }

        /// <summary>
        /// 获取指定界面的直接子界面中处于指定状态的指定类型的所有界面。
        /// </summary>
        /// <param name="nodeBehaviour">界面。</param>
        /// <param name="state">状态。它可以是多个状态进行按位与运算后的结果。</param>
        /// <param name="result">存放结果的列表。</param>
        /// <typeparam name="TNodeBehaviour">界面的类型。</typeparam>
        /// <exception cref="ArgumentNullException"><paramref name="nodeBehaviour"/> 或 <paramref name="result"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="state"/> 为 <see cref="NodeState.None"/> .</exception>
        public static void GetChildren<TNodeBehaviour>(NodeBehaviour nodeBehaviour, NodeState state, List<TNodeBehaviour> result) where TNodeBehaviour : NodeBehaviour
        {
            if (nodeBehaviour == null)
            {
                throw new ArgumentNullException(nameof(nodeBehaviour));
            }
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }
            if (state == NodeState.None)
            {
                throw new ArgumentOutOfRangeException(nameof(state), state, null);
            }
            result.Clear();
            if (nodeBehaviour.childContainer != null)
            {
                foreach (var element in nodeBehaviour.childContainer.NodeBehaviours)
                {
                    if (element is TNodeBehaviour tNodeBehaviour)
                    {
                        var tNodeBehaviourState = tNodeBehaviour.State;
                        if ((state & tNodeBehaviourState) == tNodeBehaviourState)
                        {
                            result.Add(tNodeBehaviour);
                        }
                    }
                }
            }
        }

        public static TNodeBehaviour Open<TNodeBehaviour>(OpenArgs openArgs) where TNodeBehaviour : NodeBehaviour
        {
            throw new NotImplementedException();
        }

        public static TNodeBehaviour Close<TNodeBehaviour>(CloseArgs closeArgs) where TNodeBehaviour : NodeBehaviour
        {
            throw new NotImplementedException();
        }

        private static NodeBehaviourModel Create<TNodeBehaviour>() where TNodeBehaviour : NodeBehaviour
        {
            if (_nodeBehaviourModelProviderForCreateNodeBehaviour == null)
            {
                throw new InvalidOperationException();
            }

            var nodeBehaviourModel = _nodeBehaviourModelProviderForCreateNodeBehaviour.GetNodeBehaviourModel<TNodeBehaviour>();
            if (nodeBehaviourModel == null)
            {
                throw new InvalidOperationException();
            }
            if (nodeBehaviourModel.NodeBehaviour == null)
            {
                throw new InvalidOperationException();
            }
            if (nodeBehaviourModel.AssetBundle == null)
            {
                throw new InvalidOperationException();
            }
            if (!nodeBehaviourModel.NodeBehaviour._isPrefab)
            {
                throw new InvalidOperationException();
            }
            var nodeBehaviour = Instantiate(nodeBehaviourModel.NodeBehaviour);
            nodeBehaviour.gameObject.SetActive(false);
            nodeBehaviour._isPrefab = false;
            return new NodeBehaviourModel
            {
                NodeBehaviour = nodeBehaviour,
                AssetBundle   = nodeBehaviourModel.AssetBundle
            };
        }

        #endregion

        #region instance member

        [SerializeField] private NodeBehaviourContainer     childContainer;
        [SerializeField] private NodeBehaviourCloseButton[] closeButtons;

        private bool          _isPrefab = true;
        private bool          _entering;
        private bool          _exiting;
        private RectTransform _rectTransform;
        private CanvasGroup   _canvasGroup;

        /// <summary>
        /// 状态。
        /// </summary>
        public NodeState State
        {
            get
            {
                if (_isPrefab)
                {
                    return NodeState.None;
                }
                if (!isActiveAndEnabled)
                {
                    return NodeState.Inactive;
                }
                if (_entering)
                {
                    return NodeState.Enter;
                }
                if (_exiting)
                {
                    return NodeState.Exit;
                }
                return NodeState.Active;
            }
        }

        /// <summary>
        /// 矩形变换。
        /// </summary>
        public RectTransform RectTransform => _rectTransform != null ? _rectTransform : _rectTransform = (RectTransform) transform;

        /// <summary>
        /// 画布组。
        /// </summary>
        public CanvasGroup CanvasGroup => _canvasGroup != null ? _canvasGroup : _canvasGroup = GetComponent<CanvasGroup>();

        private void PerformOpen(OpenArgs openArgs)
        {
            gameObject.SetActive(true);
            if (!isActiveAndEnabled)
            {
                return;
            }
            _exiting = false;
            if (openArgs.Immediately)
            {
                PerformOpenImmediately(openArgs);
            }
            else
            {
                StartCoroutine(PerformOpenContinuously(openArgs));
            }
        }

        private void PerformOpenImmediately(OpenArgs openArgs)
        {
            OpenImmediately(openArgs);
            Canvas.ForceUpdateCanvases();
            PostOpen(openArgs);
        }

        private IEnumerator PerformOpenContinuously(OpenArgs openArgs)
        {
            _entering = true;
            yield return OpenContinuously(openArgs);
            _entering = false;
            if (this == null || !isActiveAndEnabled)
            {
                yield break;
            }
            Canvas.ForceUpdateCanvases();
            PostOpen(openArgs);
        }

        /// <summary>
        /// 立即打开。
        /// </summary>
        /// <param name="openArgs">打开参数。</param>
        protected virtual void OpenImmediately(OpenArgs openArgs) { }

        /// <summary>
        /// 带有过程地打开。
        /// </summary>
        /// <param name="openArgs">打开参数。</param>
        /// <returns>协程枚举数。</returns>
        protected virtual IEnumerator OpenContinuously(OpenArgs openArgs)
        {
            yield break;
        }

        /// <summary>
        /// 打开完毕。
        /// </summary>
        protected virtual void PostOpen(OpenArgs openArgs) { }

        private void PerformClose(CloseArgs closeArgs)
        {
            if (!isActiveAndEnabled)
            {
                return;
            }
            _entering = false;
            if (closeArgs.Immediately)
            {
                PerformCloseImmediately(closeArgs);
            }
            else
            {
                StartCoroutine(PerformCloseContinuously(closeArgs));
            }
        }

        private void PerformCloseImmediately(CloseArgs closeArgs)
        {
            CloseImmediately(closeArgs);
            if (this != null)
            {
                switch (closeArgs.PostDestroyType)
                {
                    case DestroyType.None:
                        break;
                    case DestroyType.Destroy:
                        Destroy(gameObject);
                        break;
                    case DestroyType.DestroyImmediately:
                        DestroyImmediate(gameObject);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private IEnumerator PerformCloseContinuously(CloseArgs closeArgs)
        {
            _exiting = true;
            yield return CloseContinuously(closeArgs);
            _exiting = false;
            if (this != null)
            {
                switch (closeArgs.PostDestroyType)
                {
                    case DestroyType.None:
                        break;
                    case DestroyType.Destroy:
                        Destroy(gameObject);
                        break;
                    case DestroyType.DestroyImmediately:
                        DestroyImmediate(gameObject);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// 立即打开。
        /// </summary>
        /// <param name="closeArgs">关闭参数。</param>
        protected virtual void CloseImmediately(CloseArgs closeArgs) { }

        /// <summary>
        /// 带有过程地打开。
        /// </summary>
        /// <param name="closeArgs">关闭参数。</param>
        /// <returns>协程枚举数。</returns>
        protected virtual IEnumerator CloseContinuously(CloseArgs closeArgs)
        {
            yield break;
        }

        #region ICanvasElement

        /// <inheritdoc />
        public virtual void Rebuild(CanvasUpdate executing) { }

        /// <inheritdoc />
        public virtual void LayoutComplete() { }

        /// <inheritdoc />
        public virtual void GraphicUpdateComplete() { }

        #endregion

        #region message

        /// <inheritdoc />
        protected override void OnEnable()
        {
            base.OnEnable();
            CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
        }

        /// <summary>
        /// 阻止子类使用此成员。
        /// </summary>
        protected sealed override void Start()
        {
            base.Start();
        }

        /// <inheritdoc />
        protected override void OnDisable()
        {
            CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
            base.OnDisable();
        }

        #endregion

        #endregion
    }
}
